package web.sso.auth;

import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.web.reactive.config.WebFluxConfigurer;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Mono;
import web.sso.auth.model.Rule;
import web.sso.auth.model.RuleLoader;
import web.sso.auth.model.RuleModel;

import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * DATE: 2020/3/1
 * TIME: 12:32 上午
 * AUTHOR: hlm
 */
@Configuration
@Slf4j
public class RuleAdvice implements WebFilter, WebFluxConfigurer {

    private RequestAuth requestAuth;

    private Map<String, Rule> ruleMap = new HashMap<>();

    public RuleAdvice(
            RequestAuth requestAuth,
            RuleLoader ruleLoader
    ) {
        this.requestAuth = requestAuth;
        List<Rule> rules = ruleLoader.getRules();
        if (web.sso.common.collections.Collections.notEmpty(rules)) {
            ruleMap = rules.stream().collect(Collectors.toMap(Rule::getApi, r -> r));
        }
    }

    @Override
    public Mono<Void> filter(ServerWebExchange serverWebExchange, WebFilterChain webFilterChain) {
        ServerHttpRequest request = serverWebExchange.getRequest();
        String uri = request.getURI().toString();
        Rule rule = ruleMap.get(uri);
        if (rule == null) {
            return webFilterChain.filter(serverWebExchange);
        }
        Collection<RuleModel> authRules = rule.getAuthRules();
        RuleModel requestAuthRes = requestAuth.getAuth(serverWebExchange);
        boolean isGo = authRules.stream().anyMatch(r -> {
            boolean b = r.compare(requestAuthRes);
            if (b) {
                log.info("Api: {} is hit the rule: {}", uri, r.getRuleString());
            }
            return b;
        });
        if (isGo) {
            return webFilterChain.filter(serverWebExchange);
        }
        throw new ApiNotAccessException(uri, requestAuthRes.getRuleString());
    }

    @Bean
    @ConditionalOnMissingBean(RuleLoader.class)
    public RuleLoader defaultRuleLoader() {
        return java.util.Collections::emptyList;
    }

    @Bean
    @ConditionalOnMissingBean(RequestAuth.class)
    public RequestAuth defaultRequestAuth() {
        return exchange -> null;
    }
}
